using DeviceCommons.DeviceMessages.Models.V1;
using DeviceCommons.DeviceMessages.Enums;
using DeviceCommons.DeviceMessages.Builders;
using DeviceCommons.DataHandling;
using System.Diagnostics;
using System.Text;

namespace DeviceCommons.Tests.Shared
{
    /// <summary>
    /// 测试工具类，提供常用的测试辅助方法和断言扩展
    /// </summary>
    public static class TestUtilities
    {
        /// <summary>
        /// 验证两个消息是否在结构上相等
        /// </summary>
        /// <param name="expected">期望的消息</param>
        /// <param name="actual">实际的消息</param>
        /// <param name="compareValues">是否比较状态值</param>
        public static void AssertMessagesEqual(DeviceMessage expected, DeviceMessage actual, bool compareValues = true)
        {
            Assert.NotNull(actual);
            
            // 验证头部
            Assert.Equal(expected.Header.Version, actual.Header.Version);
            Assert.Equal(expected.Header.CRCType, actual.Header.CRCType);
            Assert.Equal(expected.Header.TimeStampFormat, actual.Header.TimeStampFormat);
            Assert.Equal(expected.Header.ValueType, actual.Header.ValueType);

            // 验证主设备
            AssertDeviceInfoEqual(expected.MainDevice, actual.MainDevice, compareValues);

            // 验证子设备
            if (expected.ChildDevice == null)
            {
                Assert.Null(actual.ChildDevice);
            }
            else
            {
                Assert.NotNull(actual.ChildDevice);
                Assert.Equal(expected.ChildDevice.Count, actual.ChildDevice.Count);
                
                for (int i = 0; i < expected.ChildDevice.Count; i++)
                {
                    AssertDeviceInfoEqual(
                        expected.ChildDevice.ChildArray[i], 
                        actual.ChildDevice.ChildArray[i], 
                        compareValues);
                }
            }
        }

        /// <summary>
        /// 验证两个消息是否在结构上相等（接受接口类型）
        /// </summary>
        /// <param name="expected">期望的消息接口</param>
        /// <param name="actual">实际的消息接口</param>
        /// <param name="compareValues">是否比较状态值</param>
        public static void AssertMessagesEqual(IDeviceMessage expected, IDeviceMessage actual, bool compareValues = true)
        {
            AssertMessagesEqual((DeviceMessage)expected, (DeviceMessage)actual, compareValues);
        }

        /// <summary>
        /// 验证两个设备信息是否相等
        /// </summary>
        /// <param name="expected">期望的设备信息</param>
        /// <param name="actual">实际的设备信息</param>
        /// <param name="compareValues">是否比较状态值</param>
        public static void AssertDeviceInfoEqual(IDeviceMessageInfo expected, IDeviceMessageInfo actual, bool compareValues = true)
        {
            Assert.NotNull(actual);
            Assert.Equal(expected.DID, actual.DID);
            Assert.Equal(expected.DeviceType, actual.DeviceType);

            // 验证读数
            if (expected.Reading == null)
            {
                Assert.Null(actual.Reading);
                return;
            }

            Assert.NotNull(actual.Reading);
            Assert.Equal(expected.Reading.ReadingArray.Length, actual.Reading.ReadingArray.Length);

            for (int i = 0; i < expected.Reading.ReadingArray.Length; i++)
            {
                AssertReadingEqual(expected.Reading.ReadingArray[i], actual.Reading.ReadingArray[i], compareValues);
            }
        }

        /// <summary>
        /// 验证两个读数是否相等
        /// </summary>
        /// <param name="expected">期望的读数</param>
        /// <param name="actual">实际的读数</param>
        /// <param name="compareValues">是否比较状态值</param>
        public static void AssertReadingEqual(IDeviceMessageInfoReading expected, IDeviceMessageInfoReading actual, bool compareValues = true)
        {
            Assert.NotNull(actual);
            Assert.Equal(expected.TimeOffset, actual.TimeOffset);

            if (expected.State == null)
            {
                Assert.Null(actual.State);
                return;
            }

            Assert.NotNull(actual.State);
            Assert.Equal(expected.State.Count, actual.State.Count);

            if (compareValues)
            {
                for (int i = 0; i < expected.State.StateArray.Length; i++)
                {
                    AssertStateEqual(expected.State.StateArray[i], actual.State.StateArray[i]);
                }
            }
        }

        /// <summary>
        /// 验证两个状态是否相等
        /// </summary>
        /// <param name="expected">期望的状态</param>
        /// <param name="actual">实际的状态</param>
        public static void AssertStateEqual(IDeviceMessageInfoReadingState expected, IDeviceMessageInfoReadingState actual)
        {
            Assert.NotNull(actual);
            Assert.Equal(expected.SID, actual.SID);
            Assert.Equal(expected.ValueType, actual.ValueType);

            switch (expected.ValueType)
            {
                case StateValueTypeEnum.Binary:
                    Assert.Equal((byte[])expected.Value, (byte[])actual.Value);
                    break;
                default:
                    Assert.Equal(expected.ValueText, actual.ValueText);
                    break;
            }
        }

        /// <summary>
        /// 在指定时间内执行操作，如果超时则抛出异常
        /// </summary>
        /// <param name="action">要执行的操作</param>
        /// <param name="timeout">超时时间</param>
        /// <param name="description">操作描述</param>
        public static void ExecuteWithTimeout(Action action, TimeSpan timeout, string description = "Operation")
        {
            var task = Task.Run(action);
            if (!task.Wait(timeout))
            {
                throw new TimeoutException($"{description} timed out after {timeout.TotalSeconds} seconds");
            }
        }

        /// <summary>
        /// 异步在指定时间内执行操作，如果超时则抛出异常
        /// </summary>
        /// <param name="asyncAction">要执行的异步操作</param>
        /// <param name="timeout">超时时间</param>
        /// <param name="description">操作描述</param>
        public static async Task ExecuteWithTimeoutAsync(Func<Task> asyncAction, TimeSpan timeout, string description = "Operation")
        {
            using var cts = new CancellationTokenSource(timeout);
            try
            {
                await asyncAction().ConfigureAwait(false);
            }
            catch (OperationCanceledException) when (cts.Token.IsCancellationRequested)
            {
                throw new TimeoutException($"{description} timed out after {timeout.TotalSeconds} seconds");
            }
        }

        /// <summary>
        /// 测量操作的内存使用情况
        /// </summary>
        /// <param name="action">要测量的操作</param>
        /// <param name="description">操作描述</param>
        /// <returns>操作前后的内存使用差值（字节）</returns>
        public static long MeasureMemoryUsage(Action action, string description = "Operation")
        {
            // 强制垃圾回收以获得准确的基准
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            var memoryBefore = GC.GetTotalMemory(false);
            
            action();

            var memoryAfter = GC.GetTotalMemory(false);
            var memoryUsed = memoryAfter - memoryBefore;

            Debug.WriteLine($"{description} - Memory used: {memoryUsed:N0} bytes");
            return memoryUsed;
        }

        /// <summary>
        /// 生成指定大小的测试数据
        /// </summary>
        /// <param name="sizeInBytes">数据大小（字节）</param>
        /// <param name="pattern">填充模式</param>
        /// <returns>生成的测试数据</returns>
        public static byte[] GenerateTestData(int sizeInBytes, byte pattern = 0x55)
        {
            var data = new byte[sizeInBytes];
            for (int i = 0; i < sizeInBytes; i++)
            {
                data[i] = (byte)(pattern + (i % 256));
            }
            return data;
        }

        /// <summary>
        /// 生成可重复的随机字符串
        /// </summary>
        /// <param name="length">字符串长度</param>
        /// <param name="seed">随机种子</param>
        /// <returns>随机字符串</returns>
        public static string GenerateRandomString(int length, int seed = 12345)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            var random = new Random(seed);
            var result = new StringBuilder(length);

            for (int i = 0; i < length; i++)
            {
                result.Append(chars[random.Next(chars.Length)]);
            }

            return result.ToString();
        }

        /// <summary>
        /// 验证字节数组是否符合预期模式
        /// </summary>
        /// <param name="data">要验证的数据</param>
        /// <param name="expectedPattern">期望的模式</param>
        /// <param name="tolerance">允许的差异数量</param>
        public static void AssertDataPattern(byte[] data, byte expectedPattern, int tolerance = 0)
        {
            Assert.NotNull(data);
            
            int differences = 0;
            for (int i = 0; i < data.Length; i++)
            {
                var expectedValue = (byte)(expectedPattern + (i % 256));
                if (data[i] != expectedValue)
                {
                    differences++;
                }
            }

            Assert.True(differences <= tolerance, 
                $"Data pattern validation failed. Expected differences <= {tolerance}, actual: {differences}");
        }

        /// <summary>
        /// 创建性能基准测试
        /// </summary>
        /// <param name="operations">操作列表</param>
        /// <param name="iterations">迭代次数</param>
        /// <param name="warmupIterations">预热迭代次数</param>
        /// <returns>性能测试结果</returns>
        public static PerformanceBenchmarkResult RunPerformanceBenchmark(
            Dictionary<string, Action> operations,
            int iterations = 1000,
            int warmupIterations = 100)
        {
            var results = new Dictionary<string, TimeSpan>();

            foreach (var (name, operation) in operations)
            {
                // 预热
                for (int i = 0; i < warmupIterations; i++)
                {
                    operation();
                }

                // 实际测试
                var stopwatch = Stopwatch.StartNew();
                for (int i = 0; i < iterations; i++)
                {
                    operation();
                }
                stopwatch.Stop();

                results[name] = stopwatch.Elapsed;
            }

            return new PerformanceBenchmarkResult(results, iterations);
        }

        /// <summary>
        /// 验证数据是否已被压缩
        /// </summary>
        /// <param name="original">原始数据</param>
        /// <param name="compressed">压缩后的数据</param>
        /// <param name="minCompressionRatio">最小压缩比</param>
        public static void AssertCompressionEffective(byte[] original, byte[] compressed, double minCompressionRatio = 0.8)
        {
            Assert.NotNull(original);
            Assert.NotNull(compressed);
            
            var compressionRatio = (double)compressed.Length / original.Length;
            Assert.True(compressionRatio < minCompressionRatio,
                $"Compression not effective. Ratio: {compressionRatio:F2}, expected < {minCompressionRatio:F2}");
        }
    }

    /// <summary>
    /// 性能基准测试结果
    /// </summary>
    public class PerformanceBenchmarkResult
    {
        public Dictionary<string, TimeSpan> Results { get; }
        public int Iterations { get; }

        public PerformanceBenchmarkResult(Dictionary<string, TimeSpan> results, int iterations)
        {
            Results = results;
            Iterations = iterations;
        }

        public TimeSpan GetAverageTime(string operationName)
        {
            return Results.TryGetValue(operationName, out var time) 
                ? TimeSpan.FromTicks(time.Ticks / Iterations)
                : TimeSpan.Zero;
        }

        public double GetOperationsPerSecond(string operationName)
        {
            var avgTime = GetAverageTime(operationName);
            return avgTime.TotalSeconds > 0 ? 1.0 / avgTime.TotalSeconds : 0;
        }

        public string GetSummary()
        {
            var summary = new StringBuilder();
            summary.AppendLine($"Performance Benchmark Results ({Iterations} iterations):");
            
            foreach (var (name, totalTime) in Results.OrderBy(r => r.Value))
            {
                var avgTime = GetAverageTime(name);
                var opsPerSec = GetOperationsPerSecond(name);
                summary.AppendLine($"  {name}: {avgTime.TotalMilliseconds:F2}ms avg, {opsPerSec:F0} ops/sec");
            }

            return summary.ToString();
        }
    }
}